var http = require('http');
var url = require('url');
var connect = require('connect');
var mysql = require('mysql');
var crypto = require('crypto');
var request = require('request');



var GOOGLE_API_KEY_SERVER = 'AIzaSyAofpfDpQSQE6R0EYDhubCAXOBQv5ldpbw';
var GOOGLE_PROFILE_URL = 'https://www.googleapis.com/oauth2/v1/userinfo';

var TWITTER_CONSUMER_KEY = '8NuqZdLwJTG8ukYVTUqjTA';
var TWITTER_CONSUMER_SECRET = 'CTG2Y3nJz77ZKjsBlttwzp8Ub1NqFGYWk8mD5RguTU';
var TWITTER_PROFILE_URL = 'https://api.twitter.com/1.1/account/verify_credentials.json';


var connection = mysql.createConnection({
	host : 'localhost',
	user : 'root',
	password : 'root',
	database : 'geocommunity',
	multipleStatements : true
});
connection.on('error', function(err) {
	if (!err.fatal) {
		return;
    }

    if (err.code !== 'PROTOCOL_CONNECTION_LOST') {
		console.log(err.stack);
		throw err;
    }
	
    console.log('Re-connecting lost connection: ' + err.stack);

    connection = mysql.createConnection(connection.config);
	connection.on('error', arguments.callee);
    connection.connect();
});
connection.connect();



/*
	store sessions in a object, clean sessions object every 10 min.
	TODO:replaced by memcache
*/
var sessions = {};//key: session id ,value: object{user:'username',update:'some time'};
setInterval(function(){
	var now = Date.now();
	var _sessions = {};
	for(var session_id in sessions){
		if(now - sessions[session_id].update < 1000 * 60 * 20){
			_sessions[session_id] = sessions[session_id];
		}
	}
	sessions = _sessions;
}, 1000 * 60 * 10);



var handlers = {
	/*
		for simple test
	*/
	'/test.service' : function(req, res){
		req.session.count = req.session.count || 0;
  		var n = req.session.count++;
  		res.end('{"result" : "viewed ' + n + ' times\n"}');
  	},
  	
  	
	/*
		google cloud messaging demo for later use.
	*/
	'/gcm.service' : function(req, res){
		var gcm = require('node-gcm');
		var sender = new gcm.Sender(GOOGLE_API_KEY_SERVER);//mapbb server api key
		var message = new gcm.Message();

		// Optional
		message.collapseKey = 'demo';
		message.addData('key1','message1');
		message.addData('key2','message2');
		message.delayWhileIdle = true;
		message.timeToLive = 3;

		/**
		 * Parameters: message-literal, registrationIds-array, No. of retries, callback-function
		 */
		sender.send(message, ['APA91bFNtwS5m0gzqnH7M8smJyVIUmizOTr50brEfwp_NlpAjn6ytWK8OBcp_k0B2CCbJ6kK6SxgX5Iq7yRSLSUyi_9IoDhMLLTj5w_lmhrj2KHgmma03yN7kOKLFZLAm2LVuaiQhd_zyLVAAeBc3HClQivdhKLp1g'], 3, function (err, result) {
    		console.log(result);
		});
   		res.end('{}');
	},
	
	
	/*
		google, facebook, twitter... login handler
	*/
	'/login_3rd.service' : function(req, res){
		if(!res.session){
			res.session = {};
		}
		if(req.query['type']=='google'){
			request.get({url : GOOGLE_PROFILE_URL + '?access_token=' + req.query['token'], encoding : 'utf-8', json : true}, function (error, response, profile) {
				if (!error && response.statusCode == 200) {
					
					console.log('google user profile:');
					console.log(profile);
					
					//use google's id of profile as username
					connection.query('SELECT id FROM user WHERE username = ? AND user_type = ?', [profile.id, 'google'], function(err, result) {
						if (!err){
							if(result.length==0){
								connection.query('INSERT INTO user(username, user_type, profile, email, last_login_time) VALUES(?, ?, ?, ?, UTC_TIMESTAMP())', [profile.id, 'google', JSON.stringify(profile), profile.email], function(err, result){
									if(!err){
										res.session.id = result.insertId;
										res.session.name = profile.name;
										res.write(JSON.stringify(res.session));
										res.end();
									}else{
										console.log(err);
										res.end(JSON.stringify({error:'database error'}));
									}
								});
							}else{
								connection.query('UPDATE user SET profile = ?, email = ?, last_login_time = UTC_TIMESTAMP() WHERE username = ? AND user_type = ?', [JSON.stringify(profile), profile.email, profile.id, 'google']);
								res.session.id = result[0].id;
								res.session.name = profile.name;
								res.write(JSON.stringify(res.session));
								res.end();
							}
						}else{
							//handle error of querying database
							console.log(err);
							res.end(JSON.stringify({error:'database error'}));
						}
					});
				}else{
					//handle error of getting profile
					console.log(error);
					res.end(JSON.stringify({error:error}));
				}
			});
		}else if(req.query['type']=='twitter'){
			var oauth = {
				consumer_key : TWITTER_CONSUMER_KEY,
				consumer_secret : TWITTER_CONSUMER_SECRET,
				token : req.query['token'],
				token_secret : req.query['secret']
			};
			request.post({url : TWITTER_PROFILE_URL, oauth : oauth, encoding : 'utf-8', json : true}, function (error, response, profile) {
				if (!error && response.statusCode == 200) {
					
					console.log('twitter user profile:');
					console.log(profile);
					res.end(JSON.stringify(profile));
				}else{
					//handle error of getting profile
					console.log(error);
					res.end(JSON.stringify({error:error}));
				}
			});
		}else{
			res.end(JSON.stringify({error:"Unknown account type."}));
		}
	},
	
	
	/*
		mapbb user login handler
	*/
	'/login.service' : function(req, res){
		/*
			User password will be hashed twice (MD5 then SHA1) before being stored in db to prevent password leaking by rainbow table searching.
			Hashed user password will be stored in local for auto login.
		*/
		
		connection.query('SELECT SHA1(MD5(?)) AS hashed_password, password FROM user WHERE username = ?', [req.query.password, req.query.username], function(err, rows) {
			var result = {};
			if (err){
				throw err;//throw err and stop service when debugging
//				result.status = 'failed';
//				result.reason = 'System error.';
			}else{
				if(rows.length==1 && ((rows[0].hashed_password==rows[0].password && req.query.mode=='manual') || (req.query.password==rows[0].password && req.query.mode=='auto'))){
					var now = Date.now();
					result.session_id = crypto.createHash('md5').update(req.query.username + now).digest('hex');
					result.status = 'OK';
					result.hashed_password = rows[0].password;
					sessions[result.session_id] = {
						user : req.query.username,
						update : now
					};
						
					connection.query('UPDATE user SET last_login_time=UTC_TIMESTAMP() WHERE username = ?', [req.query.username]);
				}else{
					result.status = 'failed';
					result.reason = 'Invalid username or password.';
				}
			}
			console.log(result);

			res.write(JSON.stringify(result));	
    		res.end();
		});
		
	},
	
	
	/*
		mapbb user register handler
	*/
	'/register.service' : function(req, res){
		console.log('query:' + url.parse(req.url).query);

		var result = {};
		if(req.query.action=='check'){
			var query = connection.query('SELECT username FROM user WHERE username = ?', [req.query.username], function(err, rows) {
				result.available = (rows.length==0 && req.query.username.length > 0) ? 'yes' : 'no';
				console.log(result);
				res.write(JSON.stringify(result));	
   				res.end();
			});
		}else if(req.query.action=='register'){
			var query = connection.query('INSERT INTO user(username, password, register_time) VALUES(?, SHA1(MD5(?)), UTC_TIMESTAMP())', [req.query.username, req.query.password], function(err, rows) {
				if (err){
					throw err;//throw err and stop service when debugging
//					result.status = 'failed';
//					result.reason = 'System error.';
				}else{
					result.status = 'OK';
				}
				res.write(JSON.stringify(result));	
    			res.end();
			});
		}
	}
};


connect()
.use(connect.logger(':method :url'))
.use(connect.query())
.use(connect.cookieParser())
.use(connect.cookieSession({ secret: 'tobo!', cookie: { maxAge:1000 * 60 * 60 * 24 * 7 }}))//req.session seems not to work
.use(function(req, res){
	var pathname = url.parse(req.url).pathname;
	res.writeHead(200, {'Content-Type': 'application/json'});
	if(typeof(handlers[pathname])=='function'){
		handlers[pathname](req, res);
	}else{
		res.end('{}');
    }
})
.listen(1888);


console.log('Server running at port 1888.');

